using System;
using System.Collections.Generic;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class allows to interpolate a polynomial out of existing reference points.
    /// </summary>
    [Serializable]
    public class PolynomialInterpolation
    {
        private readonly double[] xValues;
        private readonly double[] yValues;

        /// <summary>
        /// Initializes a new instance of the <see cref="PolynomialInterpolation"/> class.
        /// </summary>
        /// <param name="xValues">The list of x values, which defines the polynomial.</param>
        /// <param name="yValues">The list of y values, which defines the polynomial.</param>
        public PolynomialInterpolation(double[] xValues, double[] yValues)
        {
            if (xValues == (double[]) ((object) null))
            {
                throw new ArgumentNullException("xValues");
            }

            if (yValues == (double[]) ((object) null))
            {
                throw new ArgumentNullException("yValues");
            }

            if (xValues.Length != yValues.Length)
            {
                throw new Exception("The number of x values has to be even with the number of y values");
            }

            this.xValues = xValues;
            this.yValues = yValues;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PolynomialInterpolation"/> class.
        /// </summary>
        /// <param name="list">The list of points, which defines the polynomial.</param>
        public PolynomialInterpolation(Point2D[] list)
        {
            if (list == (Point2D[]) null)
            {
                throw new ArgumentNullException("list");
            }

            this.xValues = new double[list.Length];
            this.yValues = new double[list.Length];

            for (int i = 0; i < list.Length; i++)
            {
                this.xValues[i] = list[i].X;
                this.yValues[i] = list[i].Y;
            }
        }

        /// <summary>
        /// Gets the x values for the interpolation.
        /// </summary>
        /// <value>The x values for the interpolation.</value>
        public double[] XValues
        {
            get { return xValues; }
        }

        /// <summary>
        /// Gets the y values for the interpolation.
        /// </summary>
        /// <value>The y values for the interpolation.</value>
        public double[] YValues
        {
            get { return yValues; }
        }

        /// <summary>
        /// Interpolates the polynomial.
        /// </summary>
        /// <returns>The interpolated polynomial.</returns>
        public Polynomial InterpolatePolynomial()
        {
            Matrix solveMatrix = new Matrix(xValues.Length, yValues.Length + 2);

            for (int i = 0; i < xValues.Length; i++)
            {
                for (int j = 0; j < xValues.Length; j++)
                {
                    solveMatrix.SetValueAtPosition(new MatrixPosition(i, j), Math.Pow(xValues[i], xValues.Length - j));
                }

                solveMatrix.SetValueAtPosition(new MatrixPosition(i, xValues.Length), 1);
                solveMatrix.SetValueAtPosition(new MatrixPosition(i, xValues.Length + 1), yValues[i]);
            }

            double[] results = solveMatrix.SolveLinearSystem();
            List<PolynomialComponent> components = new List<PolynomialComponent>();

            for (int i = results.Length - 1; i >= 0; i--)
            {
                components.Add(new PolynomialComponent(results[i], results.Length - i));
            }

            return new Polynomial(components);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            String result = "";

            for (int i = 0; i < this.xValues.Length; i++)
            {
                result += "(" + xValues[i] + "|" + yValues[i] + ") ";
            }

            return result.Trim();
        }
    }
}